\subsubsection{ARM64}

\myparagraph{GCC}

Компилируем пример в GCC 4.8.1 для ARM64:

\lstinputlisting[numbers=left,label=hw_ARM64_GCC,caption=\NonOptimizing GCC 4.8.1 + objdump,style=customasmARM]{patterns/01_helloworld/ARM/hw.lst}

В ARM64 нет режима Thumb и Thumb-2, только ARM, так что тут только 32-битные инструкции.

Регистров тут в 2 раза больше: \myref{ARM64_GPRs}.
64-битные регистры теперь имеют префикс 
\TT{X-}, а их 32-битные части --- \TT{W-}.

\myindex{ARM!\Instructions!STP}
Инструкция \TT{STP} (\IT{Store Pair}) 
сохраняет в стеке сразу два регистра: \RegX{29} и \RegX{30}.
Конечно, эта инструкция может сохранять эту пару где угодно в памяти, но здесь указан регистр \ac{SP}, так что
пара сохраняется именно в стеке.

Регистры в ARM64 64-битные, каждый имеет длину в 8 байт, так что для хранения двух регистров нужно именно 16 байт.

Восклицательный знак (``!'') после операнда означает, что сначала от \ac{SP} будет отнято 16 и только затем
значения из пары регистров будут записаны в стек.

Это называется \IT{pre-index}.
Больше о разнице между \IT{post-index} и \IT{pre-index} 
описано здесь: \myref{ARM_postindex_vs_preindex}.

Таким образом, в терминах более знакомого всем процессора x86, первая инструкция~--- это просто аналог 
пары инструкций \TT{PUSH X29} и \TT{PUSH X30}.
\RegX{29} в ARM64 используется как \ac{FP}, а \RegX{30} 
как \ac{LR}, поэтому они сохраняются в прологе функции и
восстанавливаются в эпилоге.

Вторая инструкция копирует \ac{SP} в \RegX{29} (или \ac{FP}).
Это нужно для установки стекового фрейма функции.

\label{pointers_ADRP_and_ADD}
\myindex{ARM!\Instructions!ADRP/ADD pair}
Инструкции \TT{ADRP} и \ADD нужны для формирования адреса строки \q{Hello!} в регистре \RegX{0}, 
ведь первый аргумент функции передается через этот регистр.
Но в ARM нет инструкций, при помощи которых можно записать в регистр длинное число 
(потому что сама длина инструкции ограничена 4-я байтами. Больше об этом здесь: \myref{ARM_big_constants_loading}).
Так что нужно использовать несколько инструкций.
Первая инструкция (\TT{ADRP}) записывает в \RegX{0} адрес 4-килобайтной страницы где находится строка, 
а вторая (\ADD) просто прибавляет к этому адресу остаток.
Читайте больше об этом: \myref{ARM64_relocs}.

\TT{0x400000 + 0x648 = 0x400648}, и мы видим, что в секции данных \TT{.rodata} по этому адресу как раз находится наша
Си-строка \q{Hello!}.

\myindex{ARM!\Instructions!BL}
Затем при помощи инструкции \TT{BL} вызывается \puts. Это уже рассматривалось ранее: \myref{puts}.

Инструкция \MOV записывает 0 в \RegW{0}. 
\RegW{0} это младшие 32 бита 64-битного регистра \RegX{0}:

\input{ARM_X0_register}

А результат функции возвращается через \RegX{0}, и \main возвращает 0, 
так что вот так готовится возвращаемый результат.

Почему именно 32-битная часть?
Потому в ARM64, как и в x86-64, тип \Tint оставили 32-битным, для лучшей совместимости.

Следовательно, раз уж функция возвращает 32-битный \Tint, то нужно заполнить только 32 младших бита регистра \RegX{0}.

Для того, чтобы удостовериться в этом, немного отредактируем этот пример и перекомпилируем его.%

Теперь \main возвращает 64-битное значение:

\begin{lstlisting}[caption=\main возвращающая значение типа \TT{uint64\_t},style=customc]
#include <stdio.h>
#include <stdint.h>

uint64_t main()
{
        printf ("Hello!\n");
        return 0;
}
\end{lstlisting}

Результат точно такой же, только \MOV в той строке теперь выглядит так:

\begin{lstlisting}[caption=\NonOptimizing GCC 4.8.1 + objdump]
  4005a4:       d2800000        mov     x0, #0x0      // #0
\end{lstlisting}

\myindex{ARM!\Instructions!LDP}
Далее при помощи инструкции \INS{LDP} (\IT{Load Pair}) восстанавливаются регистры \RegX{29} и \RegX{30}.

Восклицательного знака после инструкции нет. Это означает, что сначала значения достаются из стека, и только потом \ac{SP} увеличивается на 16.

Это называется \IT{post-index}.

\myindex{ARM!\Instructions!RET}
В ARM64 есть новая инструкция: \RET. 
Она работает так же как и \INS{BX LR}, но там добавлен специальный бит,
подсказывающий процессору, что это именно выход из функции, а не просто переход, чтобы процессор
мог более оптимально исполнять эту инструкцию.

Из-за простоты этой функции оптимизирующий GCC генерирует точно такой же код.

